package com.ylz.sjzx.sio.sdk.client.core.socketio;

import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.handler.EncoderHandler;
import com.corundumstudio.socketio.protocol.Packet;
import com.corundumstudio.socketio.protocol.PacketEncoder;
import com.corundumstudio.socketio.protocol.PacketType;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wiqi
 * @version 1.0
 * @date 2021/1/14
 */
@Slf4j
public class ImEncoderHandler extends EncoderHandler {

    private final PacketEncoder encoder;

    private Configuration configuration;

    public ImEncoderHandler(Configuration configuration,
                            PacketEncoder encoder) throws IOException {
        super(configuration, encoder);
        this.encoder = encoder;
        this.configuration = configuration;
    }

    public TextWebSocketFrame imEncodePacket(Packet packet, ByteBuf out) {
        byte type = toChar(packet.getType().getValue());
        out.writeByte(type);
        ByteBuf encBuf = null;
        ByteBufOutputStream outStream = null;
        try {
            if (packet.getSubType() == PacketType.EVENT
                    || packet.getSubType() == PacketType.ACK) {

                List<Object> values = new ArrayList<Object>();
                if (packet.getSubType() == PacketType.EVENT) {
                    values.add(packet.getName());
                }
                encBuf = Unpooled.buffer(256);
                List<Object> args = packet.getData();
                values.addAll(args);
                outStream = new ByteBufOutputStream(encBuf);
                try {
                    encoder.getJsonSupport().writeValue(outStream, values);
                } catch (IOException e) {
                    log.error("imEncodePacket{}失败", e);
                    return null;
                }

                if (!encoder.getJsonSupport().getArrays().isEmpty()) {
                    packet.initAttachments(encoder.getJsonSupport().getArrays().size());
                    for (byte[] array : encoder.getJsonSupport().getArrays()) {
                        packet.addAttachment(Unpooled.wrappedBuffer(array));
                    }
                    packet.setSubType(packet.getSubType() == PacketType.ACK
                            ? PacketType.BINARY_ACK : PacketType.BINARY_EVENT);
                }
            }
            byte subType = toChar(packet.getSubType().getValue());
            out.writeByte(subType);

            if (packet.hasAttachments()) {
                byte[] ackId = PacketEncoder.toChars(packet.getAttachments().size());
                out.writeBytes(ackId);
                out.writeByte('-');
            }

            if (packet.getSubType() == PacketType.CONNECT) {
                if (!packet.getNsp().isEmpty()) {
                    out.writeBytes(packet.getNsp().getBytes(CharsetUtil.UTF_8));
                }
            } else {
                if (!packet.getNsp().isEmpty()) {
                    out.writeBytes(packet.getNsp().getBytes(CharsetUtil.UTF_8));
                    out.writeByte(',');
                }
            }

            if (packet.getAckId() != null) {
                byte[] ackId = PacketEncoder.toChars(packet.getAckId());
                out.writeBytes(ackId);
            }

            if (encBuf != null) {
                out.writeBytes(encBuf);
            }
            return new TextWebSocketFrame(out);

        } finally {
            if (encBuf.refCnt() != 0) {
                encBuf.release();
            }
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    private byte toChar(int number) {
        return (byte) (number ^ 0x30);
    }
}

